#ifndef __M_TIMERWHEEL_H__
#define __M_TIMERWHEEL_H__

#pragma once

#include "log.hpp"
#include "channel.hpp"
#include <iostream>
#include <functional>
#include <memory>
#include <vector>
#include <unordered_map>
#include <mutex>
#include <unistd.h>
#include <cstdlib>
#include <sys/timerfd.h>
#include <sys/types.h>
#include <sys/socket.h>

class EventLoop;
class TimerWheel;

class TimerTask
{
public:
    using TaskFunc = std::function<void()>;
    using ReleaseFunc = std::function<void()>;

private:
    uint64_t _id;      // 任务id
    uint32_t _timeout; // 任务超时时间
    TaskFunc _task_cb;
    ReleaseFunc _release_cb;
    bool _canceled;//定时任务是否被取消

public:
    TimerTask(uint64_t id, uint32_t timeout, const TaskFunc& task_cb)
        : _id(id),
          _timeout(timeout),
          _task_cb(task_cb),
          _canceled(false)
    {}

    ~TimerTask()
    {
        _release_cb();// 释放TimerWheel::_timer_tasks中的任务指针
        if (!_canceled)//如果定时任务没被取消则执行任务回调
             _task_cb();//handleClose()
    }

    void setReleaseFunc(const ReleaseFunc& release_cb) { _release_cb = release_cb; }

    uint32_t getTime() { return _timeout; }

    void cancel() { _canceled = true; }//取消定时任务 TODO: 取消定时任务后再刷新定时任务时间 canceled仍为false  需不需要将取消定时任务的接口设置为修改定时任务状态(取消/非取消)
};

class TimerWheel//时间轮
{
    private:
        int _tick;//_tick指针指向的位置为即将执行的定时任务
        int _size;//时间轮大小 定时任务能够设置的最大定时时间
        std::vector<std::vector<std::shared_ptr<TimerTask>>> _wheel;
        /*
        由于shared_ptr使用原始指针进行构造 其他引用了这个指针的shared_ptr的引用计数不会增加
        所以这里使用weak_ptr 让shared_ptr通过它来构造 增加其他shared_ptr的引用计数

        同时 如果使用shared_ptr来维护定时任务 那么时间轮中的任务全部释放完毕后 引用计数仍为1 任务不会析构
        就不会调用task_cb和release_cb 并且_timer_tasks中的shared_ptr只能在TimerWheel调用析构后释放并调用task_cb和release_cb
        这样一来 TimerWheel不析构 _timer_tasks会存在内存泄漏问题 同时定时任务的执行时间也乱套了 
        所以需要使用weak_ptr来维护定时任务 不占用引用计数
        */
        std::unordered_map<uint64_t, std::weak_ptr<TimerTask>> _timer_tasks;//key: 任务id  value: 任务指针
        std::mutex _mutex;
        EventLoop* _loop;
        int _timerfd;
        std::unique_ptr<Channel> _timer_channel;

    private:
        void delTimerTask(uint64_t id)//删除定时任务(从_timer_tasks中删除)
        {
            _timer_tasks.erase(id);
        }

        int createTimerFd()
        {
            int timerfd = timerfd_create(CLOCK_MONOTONIC, 0);
            if (timerfd < 0)
            {
                LOG(ERR, "create timerfd failed");
                exit(-1);
            }
            struct itimerspec itime;
            itime.it_value.tv_sec = 1;//第一次超时时间为1s后
            itime.it_value.tv_nsec = 0;
            itime.it_interval.tv_sec = 1;//第一次超时后的间隔时间为1s timerfd每秒触发一次读事件
            itime.it_interval.tv_nsec = 0;
            timerfd_settime(timerfd, 0, &itime, nullptr);
            return timerfd;
        }

        int readTimerFd()
        {
            uint64_t t;//t存放的是定时器描述符触发读事件的时候已经超时的次数
            //由于其他描述符的事件处理时间可能很长 因此在下一轮定时器描述符触发读事件时 定时器可能已经超时多次了 因此需要一次性执行完之前所有的定时任务
            int n = read(_timerfd, &t, sizeof t);
            if (n < 0)
            {
                LOG(ERR, "read timerfd failed");
                exit(-1);
            }
            return t;
        }

        void onTime()
        {
            int t = readTimerFd();
            for (int i = 0; i < t; i++)
                runTimerTask();
        }

    private:
        void runTimerTask()//执行定时任务 每秒执行一次
        {
            _tick = (_tick + 1) % _size;//秒针向后走一步
            /*
            清空秒针指向的数组 那么该数组中所有的shared_ptr都会调用析构 
            如果引用计数为0 则TimerTask析构执行定时任务并删除_timer_tasks中对应的weak_ptr 如果不为0 则说明任务刷新过定时
            */
            _wheel[_tick].clear();//TODO TimerTask的成员_timer_tasks在执行删除操作时需要保证线程安全吗
        }

        void addTimerTaskInLoop(uint64_t id, uint32_t timeout, const TimerTask::TaskFunc& task_cb)//添加定时任务
        {
            std::shared_ptr<TimerTask> task(new TimerTask(id, timeout, task_cb));//创建定时任务
            task->setReleaseFunc(std::bind(&TimerWheel::delTimerTask, this, id));
            _timer_tasks.insert(std::make_pair(id, std::weak_ptr<TimerTask>(task)));
            _wheel[(_tick + timeout) % _size].push_back(task);//根据任务设置的超时时间 将任务添加到时间轮对应位置中
        }

        void refreshTimerTaskInLoop(uint64_t id)//刷新定时任务超时时间
        {
            auto it = _timer_tasks.find(id);
            if (it == _timer_tasks.end())
                return;
            //这个shared_ptr出了当前作用域会被销毁 不会影响引用计数
            std::shared_ptr<TimerTask> task(it->second.lock());//lock获取weak_ptr管理的对象对应的shared_ptr
            //刷新超时时间并不是改变timeout 而是改变定时任务在_wheel的位置
            _wheel[(_tick + task->getTime()) % _size].push_back(task); 
        }

        void cancelTimerTaskInLoop(uint64_t id)//取消定时任务
        {
            auto it = _timer_tasks.find(id);
            /*
            当_timer_tasks中不存在id对应的任务时 要么是这个任务已经执行完了 要么是根本没有添加这个任务
            */
            if (it == _timer_tasks.end())
                return;
            std::shared_ptr<TimerTask> task(it->second.lock());
            task->cancel();//这个shared_ptr设置了TimerTask中的_canceled _wheel中的其他shared_ptr指向的都是同一块空间 看到的canceled都是true
        }
    
    public:
        TimerWheel(EventLoop* loop)
        //初始化列表的初始化顺序与初始化语句的先后顺序无关 与成员的定义顺序有关 如果_wheel如果定义在_size之前 就算_wheel的初始化语句在_size之后 也会先初始化
        : _tick(0),
          _size(60),
          _wheel(_size, std::vector<std::shared_ptr<TimerTask>>(0)),
          _loop(loop),
          _timerfd(createTimerFd()),
          _timer_channel(new Channel(_timerfd, _loop))
        {
            //_timerfd每秒触发一次读事件 设置读事件回调并开启监控 每次触发读事件时读取timerfd重置间隔时间 并让_tick向后移动一步 执行时间轮中当前时刻的任务
            _timer_channel->setReadCallback(std::bind(&TimerWheel::onTime, this));
            _timer_channel->monitorRead();
        }

        // void runTimerTask()
        // {
        //     _loop->runInLoop(std::bind(&TimerWheel::runTimerTaskInLoop, this));
        // }

        void addTimerTask(uint64_t id, uint32_t timeout, const TimerTask::TaskFunc& task_cb);

        void refreshTimerTask(uint64_t id);

        void cancelTimerTask(uint64_t id);

        bool hasTimerTask(uint64_t id);
};

#endif
